10-完整插件开发实战
# 第10章 完整插件开发实战
## 10.1 需求分析
**目标**: 开发一个"帖子点赞"插件,允许用户对帖子进行点赞/取消点赞,显示点赞数和点赞用户列表。
**功能清单**:
1. 用户可以对帖子点赞/取消点赞
2. 帖子显示点赞数
3. 点击点赞数可查看点赞用户列表
4. 后台可设置每日点赞次数限制
5. 个人中心显示"我的点赞"列表
## 10.2 插件规划
### 10.2.1 目录结构
```
my_like/
├── conf.json
├── icon.png
├── install.php
├── unstall.php
├── setting.php
├── setting.htm
├── hook/
│ ├── model_inc_file.php # 注册Model文件
│ ├── index_route_case_end.php # 注册前台路由
│ ├── my_end.php # 注册个人中心路由
│ ├── thread_plugin_body.htm # 帖子底部点赞按钮
│ ├── thread_js.htm # 点赞交互JS
│ ├── post_list_inc_username_after.htm # 回帖列表显示点赞
│ └── model_post_delete_start.php # 删帖时清理点赞
├── model/
│ └── my_like.func.php # 点赞数据模型
├── route/
│ └── like.php # 点赞路由
└── view/
└── htm/
├── my_like.htm # 我的点赞页面
└── like_users.htm # 点赞用户弹窗
```
### 10.2.2 数据库设计
```sql
-- 点赞记录表
CREATE TABLE bbs_my_like (
`lid` int(11) unsigned NOT NULL AUTO_INCREMENT,
`pid` int(11) unsigned NOT NULL default '0',
`uid` int(11) unsigned NOT NULL default '0',
`create_date` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (lid),
UNIQUE KEY (pid, uid), -- 同一用户对同一帖子只能点赞一次
KEY (uid),
KEY (pid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 给post表添加likes字段
ALTER TABLE bbs_post ADD COLUMN likes int(11) DEFAULT '0';
```
### 10.2.3 Hook点选择
| Hook点 | 用途 |
|--------|------|
| `model_inc_file.php` | 注入Model文件 |
| `index_route_case_end.php` | 注册like路由 |
| `my_end.php` | 注册个人中心"我的点赞" |
| `thread_plugin_body.htm` | 首帖点赞按钮 |
| `post_list_inc_username_after.htm` | 回帖点赞按钮 |
| `thread_js.htm` | 点赞AJAX交互 |
| `model_post_delete_start.php` | 删帖清理点赞 |
## 10.3 逐步实现
### Step 1: 创建conf.json
```json
{
"name": "帖子点赞",
"brief": "允许用户对帖子进行点赞,显示点赞数和点赞用户。",
"version": "1.0.0",
"bbs_version": "4.0",
"installed": 0,
"enable": 0,
"hooks_rank": {},
"overwrites_rank": {},
"dependencies": []
}
```
### Step 2: 创建install.php
```php
tablepre;
$sql = "CREATE TABLE IF NOT EXISTS {$tablepre}my_like (
`lid` int(11) unsigned NOT NULL AUTO_INCREMENT,
`pid` int(11) unsigned NOT NULL default '0',
`uid` int(11) unsigned NOT NULL default '0',
`create_date` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (lid),
UNIQUE KEY (pid, uid),
KEY (uid),
KEY (pid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
db_exec($sql);
$col_exists = db_find_one("SHOW COLUMNS FROM {$tablepre}post LIKE 'likes'");
if (empty($col_exists)) {
db_exec("ALTER TABLE {$tablepre}post ADD COLUMN likes int(11) DEFAULT '0';");
}
setting_set('my_like', array(
'daily_limit' => 50,
'show_users' => 1,
));
```
### Step 3: 创建unstall.php
```php
tablepre;
db_exec("DROP TABLE IF EXISTS {$tablepre}my_like;");
$col_exists = db_find_one("SHOW COLUMNS FROM {$tablepre}post LIKE 'likes'");
if (!empty($col_exists)) {
db_exec("ALTER TABLE {$tablepre}post DROP COLUMN likes;");
}
setting_delete('my_like');
```
### Step 4: 创建Model文件
```php
$pid, 'uid'=>$uid));
}
function my_like_create($pid, $uid) {
$exists = my_like_read_by_pid_uid($pid, $uid);
if ($exists) return FALSE;
$lid = my_like__create(array(
'pid' => $pid,
'uid' => $uid,
'create_date' => time(),
));
if ($lid) {
db_update('post', array('pid'=>$pid), array('likes+'=>1));
}
return $lid;
}
function my_like_delete($pid, $uid) {
$r = my_like__delete(array('pid'=>$pid, 'uid'=>$uid));
if ($r) {
db_update('post', array('pid'=>$pid), array('likes-'=>1));
}
return $r;
}
function my_like_find_by_pid($pid, $page = 1, $pagesize = 50) {
return my_like__find(array('pid'=>$pid), array('lid'=>-1), $page, $pagesize);
}
function my_like_find_by_uid($uid, $page = 1, $pagesize = 20) {
$likelist = my_like__find(array('uid'=>$uid), array('lid'=>-1), $page, $pagesize);
if ($likelist) foreach ($likelist as &$like) {
$like['post'] = post_read_cache($like['pid']);
}
return $likelist;
}
function my_like_count_by_uid($uid) {
return my_like__count(array('uid'=>$uid));
}
function my_like_count_today_by_uid($uid) {
$today_start = strtotime('today');
return my_like__count(array('uid'=>$uid, 'create_date'=>array('>'=>$today_start)));
}
```
### Step 5: 创建Hook文件
**hook/model_inc_file.php**:
```php
```
**hook/thread_js.htm**:
```html
```
**hook/model_post_delete_start.php**:
```php
$like['lid']));
}
```
### Step 6: 创建路由文件
```php
= $daily_limit) {
message(-1, '今日点赞次数已达上限');
}
$exists = my_like_read_by_pid_uid($pid, $uid);
if ($exists) {
my_like_delete($pid, $uid);
message(0, '取消点赞');
} else {
my_like_create($pid, $uid);
message(0, '点赞成功');
}
} elseif ($action == 'users') {
$pid = param(2, 0);
$likelist = my_like_find_by_pid($pid);
include _include(APP_PATH.'plugin/my_like/view/htm/like_users.htm');
} else {
message(-1, '未知操作');
}
```
### Step 7: 创建视图文件
**view/htm/my_like.htm**:
```php
```
### Step 8: 创建设置页面
**setting.php**:
```php
```
## 10.4 测试清单
| 测试项 | 预期结果 | 状态 |
|--------|---------|------|
| 安装插件 | 表创建成功,字段添加成功 | □ |
| 启用插件 | tmp缓存清空,hook注入生效 | □ |
| 帖子页显示点赞按钮 | 首帖底部出现心形图标 | □ |
| 点赞操作 | 点击后变红,数字+1 | □ |
| 取消点赞 | 再次点击变灰,数字-1 | □ |
| 重复点赞 | 同一帖子只能点赞一次 | □ |
| 每日限制 | 超过限制后提示 | □ |
| 个人中心 | 显示"我的点赞"列表 | □ |
| 删帖清理 | 删除帖子后点赞记录清除 | □ |
| 设置页面 | 可修改每日限制等配置 | □ |
| 卸载插件 | 表删除,字段删除,配置清除 | □ |
| DEBUG=0 | 生产环境下功能正常 | □ |
## 10.5 发布检查清单
- [ ] DEBUG改为0测试通过
- [ ] 删除所有调试代码(var_dump/echo等)
- [ ] conf.json版本号正确
- [ ] icon.png图标(64x64)
- [ ] PHP文件无BOM头
- [ ] 所有hook文件有`